﻿//-----------------------------------------------------------------------
// <copyright file="SortableList.cs" company="LMT">
//     Copyright (c) by La Minh Thuận.
// </copyright>
// <author>thuanla1985@hotmail.com</author>
//-----------------------------------------------------------------------
namespace LMT.Db.Collections
{
    using System;
    using System.Collections.Generic;
    using System.ComponentModel;

    /// <summary>
    /// A collection supports sorting
    /// </summary>
    /// <typeparam name="T"></typeparam>
    public class SortableList<T> : BindingList<T> where T : new()
    {
        /// <summary>
        /// 
        /// </summary>
        private bool isSorted;

        /// <summary>
        /// 
        /// </summary>
        private PropertyDescriptor sortProperty;

        /// <summary>
        /// 
        /// </summary>
        private ListSortDirection sortDirection;

        /// <summary>
        /// Initializes a new instance of the <see cref="SortableList&lt;T&gt;"/> class.
        /// </summary>
        private SortableList()
        {
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="SortableList&lt;T&gt;"/> class.
        /// </summary>
        /// <param name="collection">The collection.</param>
        public SortableList(List<T> collection)
            : this()
        {
            collection.ForEach(delegate(T enity) { this.Items.Add(enity); });
        }

        /// <summary>
        /// Gets the property descriptor that is used for sorting the list if sorting is implemented in a derived class; otherwise, returns null.
        /// </summary>
        /// <value></value>
        /// <returns>The <see cref="T:System.ComponentModel.PropertyDescriptor"/> used for sorting the list.</returns>
        protected override PropertyDescriptor SortPropertyCore
        {
            get
            {
                return this.sortProperty;
            }
        }

        /// <summary>
        /// Gets the direction the list is sorted.
        /// </summary>
        /// <value></value>
        /// <returns>One of the <see cref="T:System.ComponentModel.ListSortDirection"/> values. The default is <see cref="F:System.ComponentModel.ListSortDirection.Ascending"/>. </returns>
        protected override ListSortDirection SortDirectionCore
        {
            get
            {
                return this.sortDirection;
            }
        }

        /// <summary>
        /// Gets a value indicating whether the list supports sorting.
        /// </summary>
        /// <value></value>
        /// <returns>true if the list supports sorting; otherwise, false. The default is false.</returns>
        protected override bool SupportsSortingCore
        {
            get
            {
                return true;
            }
        }

        /// <summary>
        /// Gets a value indicating whether the list is sorted.
        /// </summary>
        /// <value></value>
        /// <returns>true if the list is sorted; otherwise, false. The default is false.</returns>
        protected override bool IsSortedCore
        {
            get
            {
                return this.isSorted;
            }
        }

        /// <summary>
        /// Sorts the items if overridden in a derived class; otherwise, throws a <see cref="T:System.NotSupportedException"/>.
        /// </summary>
        /// <param name="prop">A <see cref="T:System.ComponentModel.PropertyDescriptor"/> that specifies the property to sort on.</param>
        /// <param name="direction">One of the <see cref="T:System.ComponentModel.ListSortDirection"/>  values.</param>
        /// <exception cref="T:System.NotSupportedException">Method is not overridden in a derived class. </exception>
        protected override void ApplySortCore(PropertyDescriptor prop, ListSortDirection direction)
        {
            if (base.Items.Count > 0)
            {
                List<T> items = base.Items as List<T>;

                // Apply and set the sort, if items to sort
                if (items != null)
                {
                    PropertyComparer<T> pc = new PropertyComparer<T>(prop, direction);
                    items.Sort(pc);
                    this.isSorted = true;
                }
                else
                {
                    this.isSorted = false;
                }

                this.sortProperty = prop;
                this.sortDirection = direction;

                // Let bound controls know they should refresh their views
                this.OnListChanged(new ListChangedEventArgs(ListChangedType.Reset, -1));
            }
        }

        /// <summary>
        /// Removes any sort applied with <see cref="M:System.ComponentModel.BindingList`1.ApplySortCore(System.ComponentModel.PropertyDescriptor,System.ComponentModel.ListSortDirection)"/> if sorting is implemented in a derived class; otherwise, raises <see cref="T:System.NotSupportedException"/>.
        /// </summary>
        /// <exception cref="T:System.NotSupportedException">Method is not overridden in a derived class. </exception>
        protected override void RemoveSortCore()
        {
            this.isSorted = false;
            this.OnListChanged(new ListChangedEventArgs(ListChangedType.Reset, -1));
        }
    }
}